<!doctype html>
<html lang="en" class="dark">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Copilot API Usage Dashboard</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
      rel="stylesheet"
    />

    <script src="https://unpkg.com/lucide-react@0.378.0/dist/umd/lucide.min.js"></script>

    <style>
      /* Gruvbox-themed color palette */
      :root {
        /* Main Color Palette */
        --color-red: #cc241d;
        --color-green: #98971a;
        --color-yellow: #d79921;
        --color-blue: #458588;
        --color-purple: #b16286;
        --color-aqua: #689d6a;
        --color-orange: #d65d0e;
        --color-gray: #a89984;

        /* Accent/Lighter/Darker Shades of Main Colors */
        --color-red-accent: #fb4934;
        --color-green-accent: #b8bb26;
        --color-yellow-accent: #fabd2f;
        --color-blue-accent: #83a598;
        --color-purple-accent: #d3869b;
        --color-aqua-accent: #8ec07c;
        --color-orange-accent: #fe8019;
        --color-gray-accent: #928374;

        /* Background Colors */
        --color-bg-darkest: #1d2021; /* bg0_h */
        --color-bg: #282828; /* bg and bg0 */
        --color-bg-light-1: #3c3836; /* bg1 */
        --color-bg-light-2: #504945; /* bg2 */
        --color-bg-light-3: #665c54; /* bg3 */
        --color-bg-light-4: #7c6f64; /* bg4 */
        --color-bg-soft: #32302f; /* bg0_s */

        /* Foreground Colors */
        --color-fg-darker: #a89984; /* fg4 - duplicate of gray */
        --color-fg-dark: #bdae93; /* fg3 */
        --color-fg-medium: #d5c4a1; /* fg2 */
        --color-fg-light: #ebdbb2; /* fg and fg1 */
        --color-fg-lightest: #fbf1c7; /* fg0 */
      }

      /* Custom styles using the new palette */
      body {
        font-family: "Inter", sans-serif;
        background-color: var(--color-bg-darkest);
        color: var(--color-fg-light);
      }

      /* Custom progress bar styles */
      .progress-bar-bg {
        background-color: var(--color-bg-light-1);
      }
      .progress-bar-fg {
        transition: width 0.5s ease-in-out;
      }

      /* Custom scrollbar for the raw data view */
      .code-block::-webkit-scrollbar {
        width: 8px;
        height: 8px;
      }
      .code-block::-webkit-scrollbar-track {
        background: var(--color-bg);
      }
      .code-block::-webkit-scrollbar-thumb {
        background: var(--color-bg-light-3);
      }
      .code-block::-webkit-scrollbar-thumb:hover {
        background: var(--color-bg-light-4);
      }

      /* Style for focus rings to use variables */
      .input-focus:focus {
        --tw-ring-color: var(--color-blue);
        border-color: var(--color-blue);
      }


    </style>
  </head>
  <body class="antialiased">
    <div id="app" class="min-h-screen p-4 sm:p-6">
      <div class="max-w-7xl mx-auto">
        <!-- Header Section -->
        <header class="mb-6">
          <h1
            class="text-2xl font-bold flex items-center gap-2"
            style="color: var(--color-fg-lightest)"
          >
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              class="lucide lucide-gauge-circle h-7 w-7"
              style="color: var(--color-aqua-accent)"
            >
              <path d="M15.6 3.3a10 10 0 1 0 5.1 5.1" />
              <path
                d="M12 12a1 1 0 0 0-1-1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-3z"
              />
              <path d="M12 6.8a10 10 0 0 0 -3.2 7.2" />
            </svg>
            <span>Copilot API Usage Dashboard</span>
          </h1>
          <p class="mt-1 text-sm" style="color: var(--color-gray)">
            Should be the same as the one in VSCode
          </p>
        </header>

        <!-- Form Section -->
        <div
          class="mb-6 p-4 border"
          style="
            background-color: var(--color-bg-soft);
            border-color: var(--color-bg-light-2);
          "
        >
          <form
            id="endpoint-form"
            class="flex flex-col sm:flex-row items-center gap-3"
          >
            <label
              for="endpoint-url"
              class="font-semibold whitespace-nowrap text-sm"
              style="color: var(--color-fg-lightest)"
              >API Endpoint URL</label
            >
            <input
              type="text"
              id="endpoint-url"
              class="w-full px-3 py-1.5 border focus:ring-1 transition input-focus text-sm"
              style="
                background-color: var(--color-bg-darkest);
                border-color: var(--color-bg-light-3);
                color: var(--color-fg-medium);
              "
              placeholder="http://localhost:4141/usage"
            />
            <button
              id="fetch-button"
              type="submit"
              class="w-full sm:w-auto font-bold py-1.5 px-5 transition-colors flex items-center justify-center gap-2 text-sm"
              style="
                background-color: var(--color-blue);
                color: var(--color-bg-darkest);
              "
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="24"
                height="24"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="lucide lucide-refresh-cw h-4 w-4"
              >
                <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
                <path d="M21 3v5h-5" />
                <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
                <path d="M3 21v-5h5" />
              </svg>
              <span>Fetch</span>
            </button>
          </form>
        </div>

        <!-- Content Area for dynamic data -->
        <main id="content-area"></main>
      </div>
    </div>

    <script>
      document.addEventListener("DOMContentLoaded", () => {
        const endpointForm = document.getElementById("endpoint-form");
        const endpointUrlInput = document.getElementById("endpoint-url");
        const contentArea = document.getElementById("content-area");
        const fetchButton = document.getElementById("fetch-button");

        // Apply hover effect for fetch button via JS
        fetchButton.addEventListener("mouseenter", () => {
          fetchButton.style.backgroundColor = "var(--color-blue-accent)";
        });
        fetchButton.addEventListener("mouseleave", () => {
          fetchButton.style.backgroundColor = "var(--color-blue)";
        });

        const DEFAULT_ENDPOINT = "http://localhost:4141/usage";

        // --- State Management ---
        const state = {
          isLoading: false,
          error: null,
          data: null,
        };

        // --- Rendering Logic ---

        /**
         * Safely calls lucide.createIcons() if the library is available.
         */
        function createIcons() {
          if (typeof lucide !== "undefined") {
            lucide.createIcons();
          }
        }

        /**
         * Renders the entire UI based on the current state.
         */
        function render() {
          if (state.isLoading) {
            contentArea.innerHTML = renderSpinner();
            return;
          }
          if (state.error) {
            contentArea.innerHTML = renderError(state.error);
          } else if (state.data) {
            contentArea.innerHTML = `
                        ${renderUsageQuotas(state.data.quota_snapshots)}
                        ${renderDetailedData(state.data)}
                    `;
          } else {
            contentArea.innerHTML = renderWelcomeMessage();
          }
          // Replace placeholder icons with actual Lucide icons
          createIcons();
        }

        /**
         * Renders the "Usage Quotas" section with progress bars.
         * @param {object} snapshots - The quota_snapshots object from the API response.
         * @returns {string} HTML string for the usage quotas section.
         */
        function renderUsageQuotas(snapshots) {
          if (!snapshots) return "";

          const quotaCards = Object.entries(snapshots)
            .map(([key, value]) => {
              return renderQuotaCard(key, value);
            })
            .join("");

          return `
                    <section id="usage-quotas" class="mb-6">
                        <h2 class="text-xl font-bold mb-3 flex items-center gap-2" style="color: var(--color-fg-lightest);">
                            <i data-lucide="bar-chart-big"></i> Usage Quotas
                        </h2>
                        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
                            ${quotaCards}
                        </div>
                    </section>
                `;
        }

        /**
         * Renders a single quota card.
         * @param {string} title - The name of the quota (e.g., 'chat').
         * @param {object} details - The details object for the quota.
         * @returns {string} HTML string for a single card.
         */
        function renderQuotaCard(title, details) {
          const { entitlement, remaining, percent_remaining, unlimited } =
            details;

          const percentUsed = unlimited ? 0 : 100 - percent_remaining;
          const used = unlimited
            ? "N/A"
            : (entitlement - remaining).toLocaleString();

          let progressBarColor = "var(--color-green)";
          if (percentUsed > 75) progressBarColor = "var(--color-yellow)";
          if (percentUsed > 90) progressBarColor = "var(--color-red)";
          if (unlimited) progressBarColor = "var(--color-blue)";

          return `
                    <div class="p-4 border" style="background-color: var(--color-bg); border-color: var(--color-bg-light-2);">
                        <div class="flex justify-between items-center mb-2">
                            <h3 class="text-md font-semibold capitalize" style="color: var(--color-fg-lightest);">${title.replace(/_/g, " ")}</h3>
                            ${
                              unlimited
                                ? `<span class="px-2 py-0.5 text-xs font-medium" style="color: var(--color-blue-accent); background-color: var(--color-bg-light-1);">Unlimited</span>`
                                : `<span class="text-sm font-mono" style="color: var(--color-fg-medium);">${percentUsed.toFixed(1)}% Used</span>`
                            }
                        </div>
                        <div class="mb-3">
                             <div class="w-full progress-bar-bg h-2">
                                 <div class="progress-bar-fg h-2" style="width: ${unlimited ? 100 : percentUsed}%; background-color: ${progressBarColor};"></div>
                             </div>
                        </div>
                        <div class="flex justify-between text-xs font-mono" style="color: var(--color-fg-dark);">
                            <span>${used} / ${unlimited ? "∞" : entitlement.toLocaleString()}</span>
                            <span>${unlimited ? "∞" : remaining.toLocaleString()} remaining</span>
                        </div>
                    </div>
                `;
        }

        /**
         * Recursively builds a formatted HTML list from a JSON object.
         * @param {object} obj - The object to format.
         * @returns {string} HTML string for the formatted list.
         */
        function formatObject(obj) {
          if (obj === null || typeof obj !== "object") {
            return `<span style="color: var(--color-green-accent);">${JSON.stringify(obj)}</span>`;
          }

          return (
            '<div class="pl-4">' +
            Object.entries(obj)
              .map(([key, value]) => {
                const formattedKey = key.replace(/_/g, " ");
                let displayValue;

                if (Array.isArray(value)) {
                  displayValue =
                    value.length > 0
                      ? `<span style='color: var(--color-gray-accent)'>[...${value.length} items]</span>`
                      : `<span style='color: var(--color-gray-accent)'>[]</span>`;
                } else if (typeof value === "object" && value !== null) {
                  displayValue = formatObject(value);
                } else if (typeof value === "boolean") {
                  displayValue = `<span class="font-semibold" style="color: ${value ? "var(--color-green-accent)" : "var(--color-red-accent)"}">${value}</span>`;
                } else {
                  displayValue = `<span style="color: var(--color-blue-accent);">${JSON.stringify(value)}</span>`;
                }

                return `<div class="mt-1">
                                <span class="capitalize font-semibold" style="color: var(--color-fg-medium);">${formattedKey}:</span>
                                ${typeof value === "object" && value !== null && !Array.isArray(value) ? displayValue : ` ${displayValue}`}
                           </div>`;
              })
              .join("") +
            "</div>"
          );
        }

        /**
         * Renders the section with the full, formatted API response.
         * @param {object} data - The full API response data.
         * @returns {string} HTML string for the full data section.
         */
        function renderDetailedData(data) {
          const formattedDetails = formatObject(data);
          return `
                    <section id="detailed-data">
                        <h2 class="text-xl font-bold mb-3 flex items-center gap-2" style="color: var(--color-fg-lightest);">
                           <i data-lucide="file-text"></i> Detailed Information
                        </h2>
                        <div class="border p-4 relative font-mono text-xs" style="background-color: var(--color-bg-darkest); border-color: var(--color-bg-light-2);">
                            ${formattedDetails}
                        </div>
                    </section>
                `;
        }

        /**
         * Renders a loading spinner.
         * @returns {string} HTML string for the spinner.
         */
        function renderSpinner() {
          return `
            <div class="flex justify-center items-center py-20">
                <div class="animate-spin h-12 w-12 rounded-full border-4 border-transparent border-t-4" style="border-top-color: var(--color-blue);"></div>
            </div>`;
        }

        /**
         * Renders an error message box.
         * @param {string} message - The error message to display.
         * @returns {string} HTML string for the error message.
         */
        function renderError(message) {
          const container = document.createElement("div");
          container.className = "p-3 border";
          container.style.backgroundColor = "rgba(204, 36, 29, 0.2)";
          container.style.borderColor = "var(--color-red)";
          container.style.color = "var(--color-red-accent)";
          container.setAttribute("role", "alert");

          container.innerHTML = `
                <div class="flex items-center">
                    <i data-lucide="alert-triangle" class="h-5 w-5 mr-3"></i>
                    <div>
                        <p class="font-bold text-sm">An Error Occurred</p>
                        <p class="text-xs">${message}</p>
                    </div>
                </div>
            `;
          // Must create icons *after* innerHTML is set
          setTimeout(
            () =>
              lucide.createIcons({
                nodes: [container.querySelector("[data-lucide]")],
              }),
            0
          );
          return container.outerHTML;
        }

        /**
         * Renders a welcome message when the page first loads.
         * @returns {string} HTML string for the welcome message.
         */
        function renderWelcomeMessage() {
          return `
            <div class="text-center py-16 px-4 border" style="background-color: var(--color-bg-soft); border-color: var(--color-bg-light-2);">
                <i data-lucide="info" class="mx-auto h-10 w-10" style="color: var(--color-gray-accent);"></i>
                <h3 class="mt-2 text-lg font-semibold" style="color: var(--color-fg-lightest);">Welcome!</h3>
                <p class="mt-1 text-sm" style="color: var(--color-gray);">Enter an API endpoint URL and click "Fetch" to see usage data.</p>
            </div>
          `;
        }

        // --- Data Fetching ---

        /**
         * Fetches data from the specified API endpoint.
         */
        async function fetchData() {
          const url = endpointUrlInput.value.trim();
          if (!url) {
            state.error = "Endpoint URL cannot be empty.";
            state.isLoading = false;
            render();
            return;
          }

          state.isLoading = true;
          state.error = null;
          render();

          try {
            const response = await fetch(url);
            if (!response.ok) {
              throw new Error(
                `Request failed with status ${response.status}: ${response.statusText}`
              );
            }
            const jsonData = await response.json();
            state.data = jsonData;
          } catch (error) {
            console.error("Fetch error:", error);
            state.data = null;
            state.error = error.message;
          } finally {
            state.isLoading = false;
            render();
          }
        }

        // --- Event Handlers & Initialization ---

        /**
         * Handles the form submission to trigger a data fetch.
         * @param {Event} event - The form submission event.
         */
        function handleFormSubmit(event) {
          event.preventDefault();
          const url = endpointUrlInput.value.trim();

          // Update URL query parameter, catching potential security errors in sandboxed environments
          try {
            const currentUrl = new URL(window.location);
            currentUrl.searchParams.set("endpoint", url);
            window.history.pushState({}, "", currentUrl);
          } catch (e) {
            console.warn("Could not update URL: ", e.message);
          }

          fetchData();
        }

        /**
         * Initializes the application.
         */
        function init() {
          endpointForm.addEventListener("submit", handleFormSubmit);

          // Get endpoint from URL param on load
          const urlParams = new URLSearchParams(window.location.search);
          const endpointFromUrl = urlParams.get("endpoint");

          if (endpointFromUrl) {
            endpointUrlInput.value = endpointFromUrl;
            fetchData();
          } else {
            endpointUrlInput.value = DEFAULT_ENDPOINT;
            render(); // Render initial welcome message
          }
        }

        // Start the app
        init();
      });
    </script>

    <footer
      class="text-center py-4 text-xs"
      style="color: var(--color-gray-accent)"
    >
      <p>
        Vibe coded by
        <a
          href="https://gemini.google.com"
          target="_blank"
          rel="noopener noreferrer"
          class="underline transition-colors"
          style="color: var(--color-fg-dark)"
          onmouseover="this.style.color='var(--color-fg-light)'"
          onmouseout="this.style.color='var(--color-fg-dark)'"
        >
          Gemini
        </a>
        - Based on
        <a
          href="https://github.com/uheej0625/copilot-usage-viewer"
          target="_blank"
          rel="noopener noreferrer"
          class="underline transition-colors"
          style="color: var(--color-fg-dark)"
          onmouseover="this.style.color='var(--color-fg-light)'"
          onmouseout="this.style.color='var(--color-fg-dark)'"
        >
          copilot-usage-viewer</a
        >
      </p>
    </footer>
  </body>
</html>
